package modviewcontrol;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;

public class Node {
	enum Type{SCHEME,SINGLE,MULTI,PATTERN,IMPORT,ITEM};
	Type nodeType;
	private int parentIdx;
	private String title;
	private String regex;
	private List<Node> possChildren = new ArrayList<Node>();
	private List<Node> actualChildren = new LinkedList<Node>();
	private Color nodeColor;
	private List<StyleRange> matchedRanges = new LinkedList<StyleRange>();
	private final int NO_IDX_SPECIFIED = -1;
	private boolean expanded;

	public boolean isExpanded() {
		return expanded;
	}
	public void setExpanded(boolean expanded) {
		this.expanded = expanded;
	}
	public Node(){
		nodeType = null;
		title = null;
		regex = null;
		possChildren = new ArrayList<Node>();
		actualChildren = new LinkedList<Node>();
		nodeColor = null;
		matchedRanges = new LinkedList<StyleRange>();
		expanded = false;
	}
	public Type getNodeType() {
		return nodeType;
	}
	public void setNodeType(Type nodeType) {
		assert(nodeType!=null);

		this.nodeType = nodeType;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		//assert(title!=null);
		this.title = title;
	}
	public String getRegex() {
		return regex;
	}
	public void setRegex(String regex) {
		assert(regex!=null);
//System.out.println(this+" regex set to "+regex);
		this.regex = regex;
	}
	public List<Node> getPossChildren() {
		/*assert(!((nodeType==Type.ITEM||nodeType==Type.IMPORT)&&
		 */
		return possChildren;
	}
	public void setPossChildren(List<Node> possChildren) {
		this.possChildren = possChildren;
	}
	public boolean addPossChild(Node node){
		assert(node!=null);
		assert(nodeType!=Type.IMPORT);
		assert(possChildren!=null);
		return this.possChildren.add(node);
	}
	public boolean removePossChild(Node nodeToRemove){
		assert(nodeToRemove!=null);
		assert(possChildren.contains(nodeToRemove));
		for(int i=0; i<actualChildren.size();i++){
			Node child = actualChildren.get(i);
			if(child.parentIdx>this.possChildren.indexOf(nodeToRemove))
				child.parentIdx--;
			if(child.parentIdx==possChildren.indexOf(nodeToRemove))
				if(removeActualChild(child));
			i--;
		}
		return this.possChildren.remove(nodeToRemove);
	}
	public List<Node> getActualChildren() {
		return actualChildren;
	}
	public Node addActualChild(Node possChildNode){
		assert(possChildNode!=null);
		assert(nodeType!=Type.IMPORT);
		assert(actualChildren!=null);
		assert(possChildren.contains(possChildNode));
		Node deepCpy = deepCopy(possChildNode);
		deepCpy.parentIdx = this.possChildren.indexOf(possChildNode);
		this.actualChildren.add(deepCpy);
		return deepCpy;
	}
	@SuppressWarnings("unchecked")
	private Node deepCopy(Node other){

		assert(other!=null);
		Node deepCpyNode = new Node();
		deepCpyNode.parentIdx = NO_IDX_SPECIFIED;
		if(other.actualChildren==null)
			deepCpyNode.actualChildren = null;
		else{
			try{
				deepCpyNode.setActualChildren(other.actualChildren.getClass().newInstance());
				for(Node child: other.actualChildren)
					deepCpyNode.actualChildren.add(deepCopy(child));
			}catch(InstantiationException e){
				e.printStackTrace();
			}
			catch(IllegalAccessException e){
				e.printStackTrace();
			}
		}
		if(other.matchedRanges==null)
			deepCpyNode.matchedRanges = null;
		else{
			try{
				deepCpyNode.setMatchedRanges(other.matchedRanges.getClass().newInstance());
			}catch(InstantiationException e){}
			catch(IllegalAccessException e){}
		}
		if(other.nodeColor!=null && deepCpyNode.nodeType == Node.Type.PATTERN)
			deepCpyNode.setNodeColor(other.nodeColor);
		deepCpyNode.setNodeType(other.nodeType);
		if(other.possChildren==null)
			deepCpyNode.possChildren = null;
		else{
	
				for(Node child : other.possChildren)
					deepCpyNode.addPossChild(deepCopy(child));
			
		}
		if(other.regex!=null)
			deepCpyNode.setRegex(other.regex);
		if(other.title!=null)
			deepCpyNode.setTitle(other.title);
		return deepCpyNode;
	}
	public Node addActualChild(int possChildIndex){
		assert(possChildIndex>=0);
		assert(nodeType!=Type.IMPORT);
		assert(actualChildren!=null);
		assert(possChildren.size() > possChildIndex && possChildIndex >= 0);
		return addActualChild(possChildren.get(possChildIndex));
		
	}
	public Node addPatternChild(){
		assert(nodeType!=Type.IMPORT);
		assert(actualChildren!=null);
		Node patternNode = deepCopy(this);
		patternNode.nodeType = Type.PATTERN;
		patternNode.actualChildren.clear();
		patternNode.title = null;
		actualChildren.add(patternNode);
		return patternNode;
	}
	public Node addImportChild(){
		assert(actualChildren!=null);
		assert(nodeType!=Type.IMPORT);
		Node itemNode = deepCopy(this);
		itemNode.nodeType = Type.IMPORT;
		itemNode.actualChildren.clear();
		actualChildren.add(itemNode);
		if(itemNode.actualChildren!=null)
			itemNode.actualChildren.clear();
		if(itemNode.possChildren!=null)
			itemNode.possChildren.clear();
		assert(itemNode.possChildren.isEmpty()&&itemNode.actualChildren.isEmpty());
		return itemNode;
	}
	public boolean removeActualChild(Node nodeToRemove){
		assert(nodeToRemove!=null);
		assert(nodeType!=Type.IMPORT);
		assert(actualChildren!=null);
		return this.actualChildren.remove(nodeToRemove);
	}
	public void setActualChildren(List<Node> actualChildren) {
		this.actualChildren = actualChildren;
	}
	public Color getNodeColor() {
		return nodeColor;
	}
	public void setNodeColor(Color nodeColor) {
		if(nodeColor!=null){
			this.nodeColor = nodeColor;
		}
	}
	public List<StyleRange> getMatchedRanges() {
		return matchedRanges;
	}
	public void setMatchedRanges(List<StyleRange> matchedRanges) {
		this.matchedRanges = matchedRanges;
	}
	public void addRange(StyleRange range){
		assert(range!=null);
		assert(this.matchedRanges!=null);
		this.matchedRanges.add(range);
	}
	public String toString(){
		return title==null ? nodeType.name() : title;
	}
	public String[] toArrayString(){
		String[] tmp = new String[2];
		tmp[0] = toString();
		tmp[1] = getRegex();
		return tmp;
	}
	public String xmlHead(){
		String xmlElement = "";
		xmlElement += "<"+nodeType.name()+(title==null?"":" title=\""+title+"\"");
		xmlElement+=(regex==null?"":" regex=\""+replaceEscapes(regex)+"\"");
		xmlElement+=" parentIdx=\""+parentIdx+"\""+(nodeColor!=null?" color=\""+nodeColor+"\"":"")+">\n";
		
		return xmlElement;
	}
	public String xmlTail(){
		String result = "";
		result +="</"+nodeType.name()+">\n"; 
		return result;
	}
	public int getParentIdx(){
		return this.parentIdx;
	}
	public void setParentIdx(int parentIdx){
		this.parentIdx = parentIdx;
	}
	public String xmlSchemaHead() {
		String xmlElement = "";
		xmlElement += "<"+nodeType.name()+(title==null?"":" title=\""+title+"\"")+">\n";
		return xmlElement;
	}
	
	private String replaceEscapes(String input){
		if(input==null)
			return "";
		
		StringBuilder output = new StringBuilder();
		boolean skip=false;
		
		for(char ch : input.toCharArray()){
			if(!skip){
				if(ch=='\"'){
					output.append('\\');
				}
			}else{
				skip = !skip;
				output.append(ch);
				continue;
			}
			
			if(ch=='\\')
				skip = true;
			
			output.append(ch);
		}
		
		return output.toString();
	}
	public void setNodeColor(String string) {
		if(string.equals("")){
			nodeColor = null;
			return;
		}
		String rgb = string.substring(6);
//System.out.println("RGB: "+rgb);
		
		int i1 = rgb.indexOf("{");
		int i2 = rgb.indexOf(",");
		int i3 = rgb.lastIndexOf(",");
		int i4 = rgb.lastIndexOf("}");
		
		String strRed = rgb.substring(i1+1,i2).trim();
		String strGreen = rgb.substring(i2+1,i3).trim();
		String strBlue = rgb.substring(i3+1,i4).trim();
		
//System.out.println(strRed);
//System.out.println(strGreen);
//System.out.println(strBlue);
		
		int red = Integer.valueOf(strRed);
		int green = Integer.valueOf(strGreen);
		int blue = Integer.valueOf(strBlue);
		
		RGB colour = new RGB(red, green, blue);
		nodeColor = new Color(Display.getDefault(), colour);
	}
}
